#include <cstdio>

#include "x2tk_VText.h"

#include "os.h"

// !! This should be removed as soon as possible.
#include "global.h"

#include "x2d_ext.h"

#define ROW_SPACE 1

//int max(int a, int b) {return ((a>b)?a:b);}
//int min(int a, int b) {return ((a<b)?a:b);}

namespace x2tk {

VText::VText(int x, int y, int w, int h) : Widget(x,y,w,h), font_(X2D_PATH_DATA "TerminusFont_Black.png") {
  topLine_=0; left_=0; crow_=0; ccol_=0;
  showCursor_=true;
  autoIndent_=true;
  lineList_.add(newLine());
}

void VText::fcursor() {
  unsigned mcol = min(ccol_, lineList_[crow_].size()-1);      

  if(mcol<left_) left_=mcol;
  else if(mcol>left_+getW()/font_.getCharW()) left_=mcol-getW()/font_.getCharW();
  if(crow_<topLine_) topLine_=crow_;
  else {
    int lineH = font_.getCharH()+2*ROW_SPACE;
    if(crow_>topLine_+getH()/lineH) topLine_=crow_-getH()/lineH;
  }
}

void VText::draw(GraphicContext &g) {
  Widget::draw(g);
  
  unsigned lineH = font_.getCharH()+2*ROW_SPACE;
  
  for(int dl=0; (topLine_+dl<lineList_.size()) && (lineH*dl<getH()); dl++) {
    int l = topLine_+dl;
    unsigned length = lineList_[l].size();
    if(l%2==0) g.colorFill(128,128,128,0,lineH*dl,getW(),lineH);
    else g.colorFill(196,196,196,0,lineH*dl,getW(),lineH);
    if(length>left_) font_.drawText(g,0,lineH*dl+ROW_SPACE,&lineList_[l][left_], length-left_-1);
  }
  
  // Draw cursor.
  
  int rr = crow_-topLine_;
  int rc = min(ccol_, lineList_[crow_].size()-1)-left_;
  
  g.colorFill(0,0,0,font_.getCharW()*rc-1,lineH*rr,2,lineH);
}

// !! We should be able to read 0 character from the file.

void VText::load(const char *path) {
  FILE *file = fileOpenRead(path);

  lineList_.clear(); lineList_.add(RawVector<char>());
  int ch;
  do {
    ch=fgetc(file); if(ch=='\n' || ch==EOF) ch=0;
    lineList_.lastElement().add(ch);
    if(feof(file)) break;
    if(ch==0) lineList_.add(RawVector<char>());
  } while(true);
  fclose(file);
  ccol_=0; crow_=0;
}

void VText::event_key(const SDL_KeyboardEvent& key) {
  if(key.type==SDL_KEYDOWN) {
    switch(key.keysym.sym) {
      case SDLK_UP:
        if(!showCursor_) {
          if(topLine_>0) topLine_--;
        } else {
          if(crow_>0) crow_--;
        }
        break;
      case SDLK_DOWN:
        if(!showCursor_) {
          if(topLine_<lineList_.size()-1) topLine_++;
        } else {
          if(crow_+1<lineList_.size()) crow_++;
        }
        break;
      case SDLK_LEFT:
        if(!showCursor_) {
          if(left_>0) left_--;
        } else {
          ccol_=min(lineList_[crow_].size()-1, ccol_);
          if(ccol_>0) ccol_--;
        }
        break;
      case SDLK_RIGHT:
        if(!showCursor_) {
          left_++;
        } else {
          ccol_=min(lineList_[crow_].size()-1, ccol_);
          if(ccol_+1<lineList_[crow_].size()) ccol_++;
        }
        break;
      case SDLK_BACKSPACE:
        if(showCursor_ && (crow_<lineList_.size())) {
          ccol_ = min(lineList_[crow_].size()-1, ccol_);
          if(ccol_>0) {
            ccol_--; lineList_[crow_].removeElementAt(ccol_);
          } else if(crow_>0) {
            // Copy everything including the terminating 0.
            // !! Inefficient.
            for(unsigned i=0; i<lineList_[crow_].size()-1; i++) {
              lineList_[crow_-1].insertElementAt(lineList_[crow_][i], lineList_[crow_-1].size()-1);
            }
            ccol_=lineList_[crow_-1].size()-lineList_[crow_].size();
            lineList_.removeElementAt(crow_);
            crow_--;
          }
        }
        break;
      case SDLK_DELETE:
        if(showCursor_ && (crow_<lineList_.size())) {
          ccol_ = min(lineList_[crow_].size()-1, ccol_);
          if(ccol_<lineList_[crow_].size()-1) {
            lineList_[crow_].removeElementAt(ccol_);
          } else if(crow_+1<lineList_.size()) {
            // !! Inefficient.
            lineList_[crow_].removeElementAt(ccol_);
            for(unsigned i=0; i<lineList_[crow_+1].size(); i++) {
              lineList_[crow_].add(lineList_[crow_+1][i]);
            }
            lineList_.removeElementAt(crow_+1);
          }
        }
        break;
      case SDLK_TAB:
        if(showCursor_ &&(crow_<lineList_.size())) {
          ccol_ = min(lineList_[crow_].size()-1, ccol_);
          lineList_[crow_].insertElementAt(' ', ccol_);
          lineList_[crow_].insertElementAt(' ', ccol_);
          ccol_+=2;
        }
        break;
      case SDLK_RETURN:
        if(showCursor_ && (crow_ < lineList_.size())) {
          ccol_ = min(lineList_[crow_].size()-1, ccol_);
          
          // Insert new line.
          lineList_.insertElementAt(RawVector<char>(), crow_+1);
          
          int new_ccol = 0;
          
          // Indent.
          if(autoIndent_) {
            int i;
            for(i=0; lineList_[crow_][i]==' '; i++) lineList_[crow_+1].add(' ');
            new_ccol=i;
          }
          
          // Move content.
          // !! Inefficient.
          for(unsigned i=ccol_; i<lineList_[crow_].size()-1; i++) {
            lineList_[crow_+1].add(lineList_[crow_][i]);
          }
          // Remove content.
          // !! Inefficient.
          for(int i=lineList_[crow_].size()-1; ccol_!=i--;) lineList_[crow_].removeElementAt(i);
          lineList_[crow_+1].add(0);
          crow_++; ccol_=new_ccol;
        }
        break;
      case SDLK_HOME:
        ccol_ = 0;
        break;
      case SDLK_END:
        ccol_=max(lineList_[crow_].size()-1, 65536u);
      default:
        if(showCursor_ && (crow_<lineList_.size())) {
          Uint16 ch = key.keysym.unicode;
          if(ch==0) break;
          ccol_ = min(lineList_[crow_].size()-1, ccol_);
          lineList_[crow_].insertElementAt(static_cast<char>(ch), ccol_);
          ccol_++;
        }
    }
    fcursor();
  }
}

bool VText::mouseDown(int type) {
  bool stat = Widget::mouseDown(type);
  
  int lineH = font_.getCharH()+2*ROW_SPACE;
  int chW = font_.getCharW();
  
  if(stat) {
    if(type==1) {
      ccol_ = (getMouseX()+chW/2)/chW+left_; crow_ = getMouseY()/lineH+topLine_;
      ccol_=max(ccol_,0u); crow_=max(crow_,0u);
      crow_ = min(lineList_.size()-1, crow_);
    } else if(type==4) {
      if(topLine_>0) topLine_--;
    } else if(type==5) {
      if(topLine_+getH()/lineH<lineList_.size()) topLine_++;
    }
  }
  return stat;
}

VText::~VText() {
}

} // namespace x2tk
